﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace GameStateManagement
{
    public class StarSphere : Microsoft.Xna.Framework.GameComponent
    {
        public static int MaxStars = 4000;
        public static float DefaultDensity = 1f / 80000f;

        static float GoldenSphereConst = MathHelper.Pi * (3f - (float)Math.Sqrt(5));

        protected int m_iCurNumStars;
        protected float m_fRadius;
        protected float m_fDensity;

        // Star colors.
        protected Vector3 m_StarColor = Color.Gold.ToVector3();

        public List<Star> stars = new List<Star>();

        public StarSphere(Game game, float radius, float density)
            : base(game)
        {
            CalculateSphereSurfaceDensity();
            m_fRadius = radius;
            m_fDensity = density;
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // Create initial number of stars
            for (int i = 0; i < StarSphere.MaxStars; i++)
            {
                Star newStar = new Star(Game);
                stars.Add(newStar);
                newStar.Visible = false;
                Game.Components.Add(newStar);
            }

            base.Initialize();
        }

        protected override void Dispose(bool disposing)
        {
            // Create initial number of stars
            foreach (Star star in stars)
            {
                Game.Components.Remove(star);
            }
            stars.Clear();

            base.Dispose(disposing);
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // Determine the number of stars to distribute over the current radius of the sphere.
            CalculateSphereSurfaceDensity();

            // Sphere contraction/expansion with "Golden Section Spiral" algorithm
            float off = 2f / (float)m_iCurNumStars;
            float y, r, phi;
            Vector3 unitVec;
            for (int i = 0; i < MaxStars; i++)
            {
                if (i < m_iCurNumStars)
                {
                    y = i * off - 1 + (off / 2);
                    r = (float)Math.Sqrt(1 - y * y);
                    phi = i * GoldenSphereConst;
                    unitVec = new Vector3((float)Math.Cos(phi) * r, y, (float)Math.Sin(phi) * r);
                    stars[i].m_AmbientLightColor = m_StarColor;
                    stars[i].m_SpecularColor = m_StarColor;
                    stars[i].m_DiffuseColor = m_StarColor;
                    stars[i].m_vWorldPosition = m_fRadius * unitVec;
                    stars[i].Visible = true;
                }
                else
                {
                    stars[i].Visible = false;
                }
            }
            base.Update(gameTime);
        }

        private void CalculateSphereSurfaceDensity()
        {
            // Computer number of stars necessary to "coat" this sphere.
            float surfaceArea = 4f * MathHelper.Pi * (float)Math.Pow(m_fRadius, 2.0);
            m_iCurNumStars = (int)(surfaceArea * m_fDensity);
        }

        public float GetRadius()
        {
            return m_fRadius;
        }
    }
}